﻿// StateMachine
// State machine for handling main dialog states and actions
// 
// FLMScan - Featured by FabLab Munich

using System;

namespace FLMScan
{
    class StateMachine
    {
        #region Enums
        public enum FlmScanState
        {   EStateInvalid
        ,   EStateIdle
        ,   EStateCalibrating
        ,   EStateLaserscanning
        ,   EStateLightScanning
        ,   EStateStopping
        ,   EStatePrepareTerminating
        ,   EStateTerminating
        ,   EStateTerminated
        };

        public enum FlmScanAction
        {   EActionStartButton
        ,   EActionStopButton
        ,   EActionStopConfirm
        ,   EActionSaveButton
        ,   EActionPreviewButton
        ,   EActionCalibrateButton
        ,   EActionApplicationWindowClose        
        ,   EActionPhysicalStartStopButton
        ,   EActionStartup
        ,   EActionSwitchToLightScanning
        ,   EActionIdle
        ,   EActionApplicationWindowCloseConfirm
        ,   EActionApplicationWindowCloseCancel
        ,   EActionSetPreferences
        ,   EActionDoorContactOpen
        };
        #endregion

        private static StateMachine instance;

        private FlmScanState currentState = FlmScanState.EStateInvalid;

        private event EventHandler OnIdleStateEvent;
        private event EventHandler OnCalibratingStateEvent;        
        private event EventHandler OnLaserscanningStateEvent;
        private event EventHandler OnLightScanningStateEvent;
        private event EventHandler OnStoppingStateEvent;
        private event EventHandler OnPrepareTerminatingStateEvent;
        private event EventHandler OnTerminatingStateEvent;
        private event EventHandler OnTerminatedStateEvent;
        private event EventHandler OnSaveEvent;
        private event EventHandler OnPreviewEvent;

        public static StateMachine Instance()
        {
            if (instance == null)
                instance = new StateMachine();

            return instance;
        }

        public void Reset()
        {
            currentState = FlmScanState.EStateInvalid;
        }

        public bool HandleAction(FlmScanAction action)
        {
            bool triggerAction = true;
            return HandleAction(action, triggerAction);
        }

        public bool TryHandleAction(FlmScanAction action)
        {
            bool triggerAction = false;
            return HandleAction(action, triggerAction);
        }

        public FlmScanState CurrentState
        {
            get { return currentState; }
        }

        private bool HandleAction(FlmScanAction action, bool triggerAction)
        {
            switch (currentState)
            {
                case FlmScanState.EStateInvalid:
                    return HandleActionInvalid(action, triggerAction);

                case FlmScanState.EStateIdle:
                    return HandleActionIdle(action, triggerAction);

                case FlmScanState.EStateCalibrating:
                    return HandleActionCalibrating(action, triggerAction);

                case FlmScanState.EStateLaserscanning:
                    return HandleActionLaserscanning(action, triggerAction);

                case FlmScanState.EStateLightScanning:
                    return HandleActionLightscanning(action, triggerAction);

                case FlmScanState.EStateStopping:
                    return HandleActionStopping(action, triggerAction);

                case FlmScanState.EStatePrepareTerminating:
                    return HandleActionPrepareTerminating(action, triggerAction);

                case FlmScanState.EStateTerminating:
                    return HandleActionTerminating(action, triggerAction);

                default:
                    return false;
            }
        }

        #region Event Handlers
        public event EventHandler OnIdleState
        {
            add { OnIdleStateEvent += value; }
            remove { OnIdleStateEvent -= value; }
        }        
           
        public event EventHandler OnCalibratingState
        {
            add { OnCalibratingStateEvent += value; }
            remove { OnCalibratingStateEvent -= value; }
        }

        public event EventHandler OnLaserscanningState
        {
            add { OnLaserscanningStateEvent += value; }
            remove { OnLaserscanningStateEvent -= value; }
        }

        public event EventHandler OnLightScanningState
        {
            add { OnLightScanningStateEvent += value; }
            remove { OnLightScanningStateEvent -= value; }
        }

        public event EventHandler OnStoppingState
        {
            add { OnStoppingStateEvent += value; }
            remove { OnStoppingStateEvent -= value; }
        }

        public event EventHandler OnPrepareTerminatingState
        {
            add { OnPrepareTerminatingStateEvent += value; }
            remove { OnPrepareTerminatingStateEvent -= value; }
        }

        public event EventHandler OnTerminatingState
        {
            add { OnTerminatingStateEvent += value; }
            remove { OnTerminatingStateEvent -= value; }
        }

        public event EventHandler OnTerminatedState
        {
            add { OnTerminatedStateEvent += value; }
            remove { OnTerminatedStateEvent -= value; }
        }

        public event EventHandler OnSave
        {
            add { OnSaveEvent += value; }
            remove { OnSaveEvent -= value; }
        }

        public event EventHandler OnPreview
        {
            add { OnPreviewEvent += value; }
            remove { OnPreviewEvent -= value; }
        }        
        #endregion

        #region State Action Handlers
        private bool HandleActionInvalid(FlmScanAction action, bool triggerAction)
        {
            bool isValid = false;

            if (action == FlmScanAction.EActionStartup)
            {
                isValid = true;
                currentState = FlmScanState.EStateIdle;

                if (triggerAction)
                {
                    if (OnIdleStateEvent != null)
                    {
                        OnIdleStateEvent(this, EventArgs.Empty);
                    }
                }                
            }

            return isValid;
        }

        private bool HandleActionIdle(FlmScanAction action, bool triggerAction)
        {
            bool isValid = false;

            switch (action)
            {
                case FlmScanAction.EActionStartButton:
                    isValid = true;
                    HandleStartScanning(triggerAction);
                    break;
                case FlmScanAction.EActionStopButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionStopConfirm:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionSaveButton:
                    isValid = true;
                    HandleSave(triggerAction);
                    break;
                case FlmScanAction.EActionPreviewButton:
                    isValid = true;
                    HandlePreview(triggerAction);
                    break;
                case FlmScanAction.EActionCalibrateButton:
                    isValid = true;
                    HandleCalibrate(triggerAction);
                    break;
                case FlmScanAction.EActionApplicationWindowClose:
                    isValid = true;
                    HandlePrepareTerminating(triggerAction);
                    break;
                case FlmScanAction.EActionPhysicalStartStopButton:
                    isValid = true;
                    HandleStartScanning(triggerAction);
                    break;
                case FlmScanAction.EActionStartup:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionSwitchToLightScanning:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionIdle:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionApplicationWindowCloseConfirm:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionApplicationWindowCloseCancel:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionSetPreferences:
                    isValid = true;
                    HandleSetPreferences(triggerAction);
                    break;
                case FlmScanAction.EActionDoorContactOpen:
                    break;
            }           

            return isValid;
        }

        private void HandleStartScanning(bool triggerAction)
        {
            if (triggerAction)
            {
                currentState = FlmScanState.EStateLaserscanning;

                if (OnLaserscanningStateEvent != null)
                {
                    OnLaserscanningStateEvent(this, EventArgs.Empty);
                }
            }
        }

        private void HandleSetPreferences(bool triggerAction)
        {
            if (triggerAction)
            {
                // TBD: introduce preference state
                // current state remains the same i.e. idle

                if (OnIdleStateEvent != null)
                {
                    OnIdleStateEvent(this, EventArgs.Empty);
                }
            }
        }        

        private void HandleCalibrate(bool triggerAction)
        {
            if (triggerAction)
            {
                currentState = FlmScanState.EStateCalibrating;

                if (OnCalibratingStateEvent != null)
                {
                    OnCalibratingStateEvent(this, EventArgs.Empty);
                }
            }
        }

        private void HandlePrepareTerminating(bool triggerAction)
        {
            if (triggerAction)
            {
                currentState = FlmScanState.EStatePrepareTerminating;

                if (OnPrepareTerminatingStateEvent != null)
                {
                    OnPrepareTerminatingStateEvent(this, EventArgs.Empty);
                }
            }
        }

        private void HandleSave(bool triggerAction)
        {
            if (triggerAction)
            {
                // TBD: check whether there is something to save
                // current state remains the same i.e. idle

                if (OnSaveEvent != null)
                {
                    OnSaveEvent(this, EventArgs.Empty);
                }
            }
        }

        private void HandlePreview(bool triggerAction)
        {
            if (triggerAction)
            {
                // TBD: check whether there is something to preview

                // current state remains the same i.e. idle

                if (OnPreviewEvent != null)
                {
                    OnPreviewEvent(this, EventArgs.Empty);
                }
            }
        }



        private bool HandleActionCalibrating(FlmScanAction action, bool triggerAction)
        {
            bool isValid = false;

            switch (action)
            {
                case FlmScanAction.EActionStartButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionStopButton:
                    isValid = true;
                    HandleStopWhileCalibrating(triggerAction);
                    break;
                case FlmScanAction.EActionStopConfirm:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionSaveButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionPreviewButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionCalibrateButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionApplicationWindowClose:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionPhysicalStartStopButton:
                    isValid = true;
                    HandleStopWhileCalibrating(triggerAction);
                    break;
                case FlmScanAction.EActionStartup:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionSwitchToLightScanning:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionIdle:
                    isValid = true;
                    HandleEndCalibrating(triggerAction);
                    break;
                case FlmScanAction.EActionApplicationWindowCloseConfirm:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionApplicationWindowCloseCancel:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionSetPreferences:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionDoorContactOpen:
                    isValid = true;
                    HandleOpenDoorWhileCalibrating(triggerAction);
                    break;
            }       

            return isValid;
        }

        private void HandleStopWhileCalibrating(bool triggerAction)
        {
            if (triggerAction)
            {
                currentState = FlmScanState.EStateStopping;

                if (OnStoppingStateEvent != null)
                {
                    OnStoppingStateEvent(this, EventArgs.Empty);
                }
            }
        }

        private void HandleEndCalibrating(bool triggerAction)
        {
            if (triggerAction)
            {
                currentState = FlmScanState.EStateIdle;

                if (OnIdleStateEvent != null)
                {
                    OnIdleStateEvent(this, EventArgs.Empty);
                }
            }
        }

        private void HandleOpenDoorWhileCalibrating(bool triggerAction)
        {
            if (triggerAction)
            {
                currentState = FlmScanState.EStateIdle;

                if (OnIdleStateEvent != null)
                {
                    OnIdleStateEvent(this, EventArgs.Empty);
                }
            }
        }     

        private bool HandleActionLaserscanning(FlmScanAction action, bool triggerAction)
        {
            bool isValid = false;

            switch (action)
            {
                case FlmScanAction.EActionStartButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionStopButton:
                    isValid = true;
                    HandleStopWhileScanning(triggerAction);
                    break;
                case FlmScanAction.EActionStopConfirm:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionSaveButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionPreviewButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionCalibrateButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionApplicationWindowClose:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionPhysicalStartStopButton:
                    isValid = true;
                    HandleStopWhileScanning(triggerAction);
                    break;
                case FlmScanAction.EActionStartup:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionSwitchToLightScanning:
                    isValid = true;
                    HandleSwitchToLightScanning(triggerAction);
                    break;
                case FlmScanAction.EActionIdle:
                    isValid = true;
                    HandleEndLaserscanning(triggerAction);
                    break;
                case FlmScanAction.EActionApplicationWindowCloseConfirm:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionApplicationWindowCloseCancel:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionSetPreferences:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionDoorContactOpen:
                    isValid = true;
                    HandleOpenDoorWhileScanning(triggerAction);
                    break;
            }       

            return isValid;
        }
        
        private void HandleEndLaserscanning(bool triggerAction)
        {
            if (triggerAction)
            {
                currentState = FlmScanState.EStateIdle;

                if (OnIdleStateEvent != null)
                {
                    OnIdleStateEvent(this, EventArgs.Empty);
                }
            }
        }

        private void HandleSwitchToLightScanning(bool triggerAction)
        {
            if (triggerAction)
            {
                currentState = FlmScanState.EStateLightScanning;

                if (OnLightScanningStateEvent != null)
                {
                    OnLightScanningStateEvent(this, EventArgs.Empty);
                }
            }
        }

        private void HandleStopWhileScanning(bool triggerAction)
        {
            if (triggerAction)
            {
                currentState = FlmScanState.EStateStopping;

                if (OnStoppingStateEvent != null)
                {
                    OnStoppingStateEvent(this, EventArgs.Empty);
                }
            }
        }        

        private void HandleOpenDoorWhileScanning(bool triggerAction)
        {
            if (triggerAction)
            {
                currentState = FlmScanState.EStateIdle;

                if (OnIdleStateEvent != null)
                {
                    OnIdleStateEvent(this, EventArgs.Empty);
                }
            }
        }        

        private bool HandleActionLightscanning(FlmScanAction action, bool triggerAction)
        {
            bool isValid = false;

            switch (action)
            {
                case FlmScanAction.EActionStartButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionStopButton:
                    isValid = true;
                    HandleStopWhileScanning(triggerAction);
                    break;
                case FlmScanAction.EActionStopConfirm:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionSaveButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionPreviewButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionCalibrateButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionApplicationWindowClose:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionPhysicalStartStopButton:
                    isValid = true;
                    HandleStopWhileScanning(triggerAction);
                    break;
                case FlmScanAction.EActionStartup:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionSwitchToLightScanning:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionIdle:                    
                    isValid = true;
                    HandleEndLightScanning(triggerAction);
                    break;
                case FlmScanAction.EActionApplicationWindowCloseConfirm:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionApplicationWindowCloseCancel:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionSetPreferences:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionDoorContactOpen:
                    isValid = true;
                    HandleOpenDoorWhileScanning(triggerAction);
                    break;
            }                  

            return isValid;
        }

        private void HandleEndLightScanning(bool triggerAction)
        {
            if (triggerAction)
            {
                currentState = FlmScanState.EStateIdle;

                if (OnIdleStateEvent != null)
                {
                    OnIdleStateEvent(this, EventArgs.Empty);
                }
            }
        }
        
        private bool HandleActionStopping(FlmScanAction action, bool triggerAction)
        {
            bool isValid = false;

            switch (action)
            {
                case FlmScanAction.EActionStartButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionStopButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionStopConfirm:
                    isValid = true;
                    HandleStopConfirm(triggerAction);
                    break;
                case FlmScanAction.EActionSaveButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionPreviewButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionCalibrateButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionApplicationWindowClose:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionPhysicalStartStopButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionStartup:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionSwitchToLightScanning:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionIdle:                    
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionApplicationWindowCloseConfirm:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionApplicationWindowCloseCancel:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionSetPreferences:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionDoorContactOpen:
                    // not valid - ignore
                    break;
            }                  

            return isValid;
        }

        private void HandleStopConfirm(bool triggerAction)
        {
            if (triggerAction)
            {
                currentState = FlmScanState.EStateIdle;

                if (OnIdleStateEvent != null)
                {
                    OnIdleStateEvent(this, EventArgs.Empty);
                }
            }
        }

        private bool HandleActionPrepareTerminating(FlmScanAction action, bool triggerAction)
        {
            bool isValid = false;

            switch (action)
            {
                case FlmScanAction.EActionStartButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionStopButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionStopConfirm:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionSaveButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionPreviewButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionCalibrateButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionApplicationWindowClose:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionPhysicalStartStopButton:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionStartup:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionSwitchToLightScanning:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionIdle:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionApplicationWindowCloseConfirm:
                    isValid = true;
                    HandleTermination(triggerAction);
                    break;
                case FlmScanAction.EActionApplicationWindowCloseCancel:
                    isValid = true;
                    HandleCancelledTermination(triggerAction);
                    break;
                case FlmScanAction.EActionSetPreferences:
                    // not valid - ignore
                    break;
                case FlmScanAction.EActionDoorContactOpen:
                    // not valid - ignore
                    break;
            }            
            
            return isValid;
        }

        private void HandleTermination(bool triggerAction)
        {
            if (triggerAction)
            {
                currentState = FlmScanState.EStateTerminated;

                if (OnTerminatingStateEvent != null)
                {
                    OnTerminatingStateEvent(this, EventArgs.Empty);
                }
            }
        }

        private void HandleCancelledTermination(bool triggerAction)
        {
            if (triggerAction)
            {
                currentState = FlmScanState.EStateIdle;

                if (OnIdleStateEvent != null)
                {
                    OnIdleStateEvent(this, EventArgs.Empty);
                }
            }
        }        

        private bool HandleActionTerminating(FlmScanAction action, bool triggerAction)
        {
            bool isValid = false;


            if (triggerAction)
            {
                if (OnTerminatedStateEvent != null)
                {
                    OnTerminatedStateEvent(this, EventArgs.Empty);
                }
            }

            return isValid;
        }
        #endregion
    }
}
